//
// Created on 2025/6/17.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "program_wrapper.h"


ProgramWrapper::ProgramWrapper() {OH_LOG_INFO(LOG_APP, "ProgramWrapper constructor called.");}
ProgramWrapper::~ProgramWrapper() {OH_LOG_INFO(LOG_APP, "ProgramWrapper destructor called.");}

void ProgramWrapper::checkError(cl_int err, const char *msg) {
    if (err != CL_SUCCESS) {
        // 打印错误代码
        OH_LOG_ERROR(LOG_APP, "Failed to create kernel, %{public}s returned error: %{public}d", msg, err);
    } else {
        OH_LOG_INFO(LOG_APP, "%{public}s succeeded.", msg);
    }
}


void ProgramWrapper::runMethod() {

    bool cl_ok = OHOS::InitOpenCL();
    OH_LOG_INFO(LOG_APP, "InitOpenCL:%{public}d.", cl_ok);
    // 获取设备信息
    cl_int err;
    cl_platform_id platform_id;
    cl_device_id device_id;
    //     1. 获取opencl平台和设备
    clGetPlatformIDs(1, &platform_id, NULL);
    clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL);
    //     1.1 打印设备信息
    char device_name[128];
    char device_version[128];

    // 获取设备名称
    err = clGetDeviceInfo(device_id, CL_DEVICE_NAME, sizeof(device_name), &device_name, NULL);
    if (err == CL_SUCCESS) {
        OH_LOG_INFO(LOG_APP, "Device Name: %{public}s", device_name);
    } else {
        OH_LOG_ERROR(LOG_APP, "Failed to get Device Name.");
    }

    // 获取设备版本
    err = clGetDeviceInfo(device_id, CL_DEVICE_VERSION, sizeof(device_version), &device_version, NULL);
    if (err == CL_SUCCESS) {
        OH_LOG_INFO(LOG_APP, "Device Version: %{public}s", device_version);
    } else {
        OH_LOG_ERROR(LOG_APP, "Failed to get Device Version.");
    }
    //     2. 创建OpenCL上下文
    std::string compileOption_ = "-cl-fast-relaxed-math -D USE_CUSTOM_MACRO=1";
    cl_context context_ = clCreateContext(0, 1, &device_id, NULL, NULL, &err);
    //     3. 创建命令队列
    //     cl_command_queue queue_ = clCreateCommandQueueWithProperties(context_, device_id, 0, &err);
    cl_command_queue queue_ = clCreateCommandQueue(context_, device_id, 0, &err);
    checkError(err, "clCreateCommandQueue");

    //     4. 创建并编译OpenCL程序
    cl_program program = clCreateProgramWithSource(context_, 1, &program_source, nullptr, nullptr);
    err = clBuildProgram(program, 1, &device_id, compileOption_.c_str(), NULL, NULL);
    checkError(err, "clBuildProgram");

    //     5. 创建内核对象，对应上述源码中的函数名
    cl_kernel kernel_ = clCreateKernel(program, "add_two_numbers", &err);
    checkError(err, "clCreateKernel");

    //     6. 创建变量缓冲区
    cl_mem bufferAdd1 =
        clCreateBuffer(context_, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, N * sizeof(int), add1.data(), NULL);
    cl_mem bufferAdd2 =
        clCreateBuffer(context_, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, N * sizeof(int), add2.data(), NULL);
    cl_mem bufferResult = clCreateBuffer(context_, CL_MEM_WRITE_ONLY, N * sizeof(int), NULL, NULL);

    //     7. 设置内核参数
    err = clSetKernelArg(kernel_, 0, sizeof(cl_mem), (void *)&bufferAdd1);
    checkError(err, "bufferAdd1");
    err = clSetKernelArg(kernel_, 1, sizeof(cl_mem), (void *)&bufferAdd2);
    checkError(err, "bufferAdd2");
    err = clSetKernelArg(kernel_, 2, sizeof(cl_mem), (void *)&bufferResult);
    checkError(err, "bufferResult");

    //     8. 执行内核
    size_t global_size = N; // 全局工作项总数
    err = clEnqueueNDRangeKernel(queue_, kernel_, 1, NULL, &global_size, NULL, 0, NULL, NULL);
    checkError(err, "enqueue kernel");
    clFinish(queue_);
    //     9. 读取结果
    err = clEnqueueReadBuffer(queue_, bufferResult, CL_TRUE, 0, N * sizeof(int), result.data(), 0, NULL, NULL);
    checkError(err, "read buffer");

    //     10. 打印结果
    OH_LOG_INFO(LOG_APP, "Result: %{public}d", result[0]);
    //     11. 释放资源
    clReleaseMemObject(bufferAdd1);
    clReleaseMemObject(bufferAdd2);
    clReleaseMemObject(bufferResult);
    clReleaseKernel(kernel_);
    clReleaseProgram(program);
    clReleaseCommandQueue(queue_);
    clReleaseContext(context_);
}